package com.zjpavt.client.job;

import com.zjpavt.client.Client;
import com.zjpavt.client.ConnectionService;
import com.zjpavt.client.command.kcommand.KCommandManager;
import com.zjpavt.client.command.offlinecommand.OfflineCommandBean;
import com.zjpavt.client.command.offlinecommand.OfflineCommandManager;
import com.zjpavt.client.command.todaycommand.TodayCommandManager;
import com.zjpavt.client.hal.LightIntensityController;
import com.zjpavt.client.hal.LoopManager;
import com.zjpavt.client.job.util.TriggerUtil;
import com.zjpavt.client.util.LoggerUtil;
import com.zjpavt.client.util.StringUtil;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.rmi.activation.ActivateFailedException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 所有运行指令切换类
 *
 * @author gxy
 */
public class JobRunner {
    static final Logger log = LoggerFactory.getLogger(JobRunner.class);
    /**
     * 正在执行带有效时长的k指令
     */
    static final int EXECUTOR_K_WITHTIME_COMMAND_STATE = 5;
    /**
     * 正在执行不带有效时长的k指令
     */

    static final int EXECUTOR_K_NOTWITHTIME_COMMAND_STATE = 4;
    /**
     * 正在执行光照强度指令
     */

    public static final int EXECUTOR_P_LIGHTINTENSITY_COMMAND_STATE = 3;
    /**
     * 正在执行当天定时器指令
     */
    public static final int EXECUTOR_TODAYTIMER_COMMAND_STATE = 2;
    /**
     * 正在执行离线指令
     */
    static final int EXECUTOR_OFFLINE_COMMAND_STATE = 1;
    /**
     * 正在执行全关指令
     */
    public static final int EXECUTOR_ALLCLOSE_COMMAND_STATE = 0;
    /**
     * 还没有开始执行指令的状态
     */
    public static final int EXECUTOR_NOT_START = -1;

    private final JobManager jobManager = JobManager.getInstance();
    private final ReentrantLock lock = new ReentrantLock(true);
    /**
     * 全关指令
     */
    private String allClose = null;
    /**
     * 全局获取当前状态
     */
    private volatile int state = -1;
    private static final JobRunner jobRunner = new JobRunner();
    private final LoopManager loopManager;
    private ScheduledThreadPoolExecutor jobRunnerThreadPool = new ScheduledThreadPoolExecutor(4, new DefaultThreadFactory("jobRunnerThreadPool"));
    public int getState() {
        return state;
    }

    private JobRunner() {
        StringBuilder sb = new StringBuilder();
        loopManager = LoopManager.getInstance();
        int loopNum = loopManager.getLoopNum();
        for (int i = 0; i < loopNum; i++) {
            sb.append("0");
        }
        allClose = sb.toString();
        //根据与服务器的连接状态调整 job runner的运行状态
        ConnectionService.getInstance().addListener(new ConnectionService.ChannelListener() {
            @Override
            public void onActive(boolean isActive) {
                if (isActive) {
                    //do nothing
                } else {
                    KCommandManager.getInstance().clearKCommand();
                }
            }

            @Override
            public void onReceiveData() {
            }
        });
        //延迟两分钟启动指令扫描 收到k指令时执行
        log.info("don,t start switch command time");
        jobRunnerThreadPool.schedule(() -> JobRunner.getInstance().switchToKCommandWithTime(), 60, TimeUnit.SECONDS);
    }

    public void setState(int state) {
        this.state = state;
    }

    public static JobRunner getInstance() {
        return jobRunner;
    }

    /**
     * 尝试刷新执行状态 如果还没有启动 则忽略
     */
    public void tryStartJobRunner() {
        if (getState() < 0) {
            return;
        } else {
            switchToKCommandWithTime();
        }
    }
    /**
     * 处理带有效时长的k指令
     */
    public void switchToKCommandWithTime() {
        KCommandManager.KCommandBean kCommandBean = KCommandManager.getInstance().getKCommandBeanWithTime();
        lock.lock();
        try {
            log.debug("Switch to k command with time");
            setState(EXECUTOR_K_WITHTIME_COMMAND_STATE);
            long time = kCommandBean.getEndTime();
            if (time != 0) {
                long timeLeft = time - Instant.now().toEpochMilli();
                if (timeLeft > 0) {
                    if (!loopManager.changeAutoLoopStatus(StringUtil.commandStr2Int(kCommandBean.getCommand()))) {
                        log.error("change loopStates fail");
                        switchToKCommandWithoutTime();
                        return;
                    }
                    jobManager.addTempJobTriggerAndExec(JobManager.TIMING_JOB_TYPE_K_NOTWITHTIME_COMMAND,
                                                        TriggerUtil.getTrigger(time));
                    return;
                }
            }
        } catch (ParseException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
        finally {
            lock.unlock();
        }
        switchToKCommandWithoutTime();
    }

    /**
     * 处理不带有效时常的k指令
     */
    public void switchToKCommandWithoutTime() {
        KCommandManager.KCommandBean kCommandBean = KCommandManager.getInstance().getKCommandBean();
        lock.lock();
        try {
            log.debug("Switch to k command without time");
            setState(EXECUTOR_K_NOTWITHTIME_COMMAND_STATE);
            long time = kCommandBean.getEndTime();
            if (time != 0) {
                long timeLeft = time - Instant.now().toEpochMilli();
                if (timeLeft > 0) {
                    if (!loopManager.changeAutoLoopStatus(StringUtil.commandStr2Int(kCommandBean.getCommand()))) {
                        log.error("change loopStates fail");
                        switchToTodayTimerCommand();
                        return;
                    }
                    jobManager.addTempJobTriggerAndExec(JobManager.TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND,
                                                        TriggerUtil.getTrigger(time));
                    return;
                }
            }
        } catch (ParseException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
        finally {
            lock.unlock();
        }
        switchToTodayTimerCommand();
    }

    /**
     * 尝试刷新定时器指令，判断当前状态，优先级高于定时器指令则不执行
     */
    public void refreshLightlntensityCommand() {
        if (state > EXECUTOR_P_LIGHTINTENSITY_COMMAND_STATE) {
            log.debug("Current status priority higher than lightIntensity Command,not switch");
            return;
        }
        switchToLightlntensityCommand();
    }

    /**
     * 切换到光照强度指令
     */
    public void switchToLightlntensityCommand() {
        final LightIntensityController instance = LightIntensityController.getInstance();
        lock.lock();
        try {
            log.debug("Switch to light intensity");
            setState(EXECUTOR_P_LIGHTINTENSITY_COMMAND_STATE);
            try {
                String lightIntensity = instance.getFormattedLightIntensity();
                LightIntensityController.LightIntensityBean lightIntensityBean = instance.getLightIntensityBean();
                String command = lightIntensityBean.getLightCommand();
                if (lightIntensity == null || command == null) {
                    switchToTodayTimerCommand();
                    return;
                }
                if (!LoopManager.getInstance().changeAutoLoopStatus(StringUtil.commandStr2Int(command))) {
                    throw new ActivateFailedException("change ChanelStatus fail");
                }
                long endTime = Instant.now().toEpochMilli() + (long) (lightIntensityBean.getLightCommandLastTime() * 1000);
                instance.setStopLightCommandTime(endTime);
                jobManager.addTempJobTriggerAndExec(JobManager.TIMING_JOB_TYPE_SWITCH_TO_TIMER_COMMAND,
                                                    TriggerUtil.getTrigger(endTime));
            } catch (ParseException | ActivateFailedException e) {
                log.error(LoggerUtil.convertException2String(e));
                switchToTodayTimerCommand();
            }
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * 尝试刷新定时器指令，判断当前状态，优先级高于定时器指令则不执行
     */
    public void refreshTodayTimerCommand() {
        //当前指令等级大于定时器指令
        if (state > EXECUTOR_TODAYTIMER_COMMAND_STATE) {
            log.debug("Current status priority higher than Today Timer Command,not switch");
            return;
        }
        switchToTodayTimerCommand();
    }

    /**
     * 执行当天定时指令
     */
    public void switchToTodayTimerCommand() {
        final TodayCommandManager instance = TodayCommandManager.getInstance();
        lock.lock();
        try {
            log.trace("switch to today timer command");
            setState(EXECUTOR_TODAYTIMER_COMMAND_STATE);
            String nowTodayCommand = instance.getNowTodayCommand(LocalTime.now());
            if (nowTodayCommand == null) {
                switchToOfflineCommand();
                return;
            }
            if (!loopManager.changeAutoLoopStatus(StringUtil.commandStr2Int(nowTodayCommand))) {
                log.error("change loopStates fail");
                switchToOfflineCommand();
            }
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * 发布当前定时器
     */
    public void sendTodayTimerCommand() {
        log.trace("try to send today timer command");
        final TodayCommandManager todayCommandManager = TodayCommandManager.getInstance();
        lock.lock();
        try {
            String todayTimer = todayCommandManager.getTodayTimer();
            String[] split = todayTimer.split("_");
            if (split[0].equals(split[1])) {
                return;
            }
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
                LocalTime startTime = LocalTime.parse(split[0], formatter);
                LocalTime endTime = LocalTime.parse(split[1], formatter);
                jobManager.addTodayTriggerAndExec(JobManager.TIMING_JOB_TYPE_REFRESH_TODAY_TIMER_COMMAND,
                                                  TriggerUtil.getDailyTrigger(startTime.getHour(), startTime.getMinute(), startTime.getSecond()));
                jobManager.addTodayTriggerAndExec(JobManager.TIMING_JOB_TYPE_CLOSE_TODAY_TIMER_COMMAND,
                                                  TriggerUtil.getDailyTrigger(endTime.getHour(), endTime.getMinute(), endTime.getSecond()));
            } catch (NumberFormatException e) {
                log.error(LoggerUtil.convertException2String(e));
            }
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * 尝试刷新离线指令，判断当前状态，优先级高于离线指令则不执行
     */
    public void refreshOfflineCommand() {
        //当前指令等级大于离线指令
        if (state > EXECUTOR_OFFLINE_COMMAND_STATE) {
            log.debug("Current status priority higher than offline Command,not switch");
            return;
        }
        switchToOfflineCommand();
    }

    /**
     * 当k指令失效后找到当前需要的离线指令，写入离线指令，此处不判断当前状态
     */
    public void switchToOfflineCommand() {
        lock.lock();
        try {
            log.trace("switch to offline command");
            setState(EXECUTOR_OFFLINE_COMMAND_STATE);
            OfflineCommandBean offlineCommandBean = OfflineCommandManager.readOfflineCommandByTime(new Date());
            if (offlineCommandBean == null) {
                switchToAllClose();
                return;
            }
            if (!loopManager.changeAutoLoopStatus(StringUtil.commandStr2Int(offlineCommandBean.getCommand()))) {
                log.error("change loopStates fail");
                switchToAllClose();
            }
        } catch (IOException | ParseException e) {
            log.error(LoggerUtil.convertException2String(e));
            switchToAllClose();
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * 所有指令都不存在时，切换到全关指令
     */
    public void switchToAllClose() {
        lock.lock();
        try {
            log.trace("switch to AllClose ");
            log.debug("don't use all close any more");
            setState(EXECUTOR_ALLCLOSE_COMMAND_STATE);
            String closeCommand = LoopManager.getInstance().getAllClosedCommand();
            if (!loopManager.changeAutoLoopStatus(StringUtil.commandStr2Int(closeCommand))) {
                log.error("change loopStates fail,AllClose fail");
            }
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * 发起当天剩余的执行计划任务
     */
    public void sendOfflineCommandOfToday() {
        try {
            List<OfflineCommandBean> list = OfflineCommandManager.readOfflineCommandListByDay(new Date());
            for (OfflineCommandBean bean : list) {
                if (!bean.isOutOfDate()) {
                    execOfflineCommand(bean);
                }
            }
        } catch (IOException | ParseException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * 发起单条离线指令的执行计划任务
     */
    public void execOfflineCommand(OfflineCommandBean bean) {
        try {
            jobManager.addOfflineCommandTriggerAndExec(TriggerUtil.getTrigger(bean.getDate().getTime()));
        } catch (ParseException e) {
            log.error(LoggerUtil.convertException2String(e));
        }
    }

    /**
     * @return 当天正在指令指令的倒计时时间
     */
    public String getCountDownTime() {
        String result = "00000000";
        switch (state) {
        case EXECUTOR_ALLCLOSE_COMMAND_STATE:
            break;
        case EXECUTOR_OFFLINE_COMMAND_STATE:
//				对于离线指令 剩余执行时间为下一个时间点的时间
            try {
                OfflineCommandBean nowOfflineCommandBean = OfflineCommandManager.readNextOfflineCommand(new Date());
                long millTime1 = nowOfflineCommandBean.getDate().getTime() - System.currentTimeMillis();
                result = formatTime(millTime1, result);
            } catch (IOException | ParseException | NullPointerException e) {
                log.error(LoggerUtil.convertException2String(e));
                return result;
            }
            break;
        case EXECUTOR_TODAYTIMER_COMMAND_STATE:
            LocalTime localCloseTime = TodayCommandManager.getInstance().getLocalCloseTime();
            LocalTime nowTime = LocalTime.now();
            long millTime2 = Duration.between(nowTime, localCloseTime).toMillis();
            result = formatTime(millTime2, result);
            break;
        case EXECUTOR_P_LIGHTINTENSITY_COMMAND_STATE:
            long millTime3 = LightIntensityController.getInstance().getStopLightCommandTime() - Instant.now().toEpochMilli();
            result = formatTime(millTime3, result);
            break;
        case EXECUTOR_K_NOTWITHTIME_COMMAND_STATE:
            try {
                long millTime4 = KCommandManager.getInstance().getKCommandBean().getEndTime() - Instant.now().toEpochMilli();
                result = formatTime(millTime4, result);
            } catch (NumberFormatException e) {
                log.error(LoggerUtil.convertException2String(e));
            }
            break;
        case EXECUTOR_K_WITHTIME_COMMAND_STATE:
            try {
                long millTime4 = KCommandManager.getInstance().getKCommandBeanWithTime().getEndTime() - Instant.now().toEpochMilli();
                result = formatTime(millTime4, result);
            } catch (NumberFormatException e) {
                log.error(LoggerUtil.convertException2String(e));
            }
            break;
        default:
            break;
        }
        return result;
    }

    private String formatTime(long millTime, String result) {
        final long MAX = 99999999;
        if (millTime < 0) {
            return result;
        }
        long time = millTime / 1000;
        if (time > MAX) {
            result = String.valueOf(MAX);
        } else {
            DecimalFormat df = new DecimalFormat(result);
            result = df.format(time);
        }
        return result;
    }
}
